package com.zc.gateway.security.access;

import com.zc.commons.auth.dto.CurrentAuthUser;
import com.zc.commons.auth.exception.AnonymousAccessDeniedException;
import com.zc.gateway.infra.service.PattenRoleMemoryCacheService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authorization.AuthorizationDecision;
import org.springframework.security.authorization.ReactiveAuthorizationManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import reactor.core.publisher.Mono;

import java.util.Set;

/**
 * 角色校验器
 *
 * @author zcj
 * @version 1.0.0
 * @date 2022/1/6 15:21
 */
@Component
public class RoleAccessDecisionManager implements ReactiveAuthorizationManager<String> {

    @Autowired
    private PattenRoleMemoryCacheService pattenRoleMemoryCacheService;

    @Override
    public Mono<AuthorizationDecision> check(Mono<Authentication> authenticationMono, String url) {
        Set<String> roles = pattenRoleMemoryCacheService.getPattenRoles(url);
        Authentication authentication = authenticationMono.block();
        // 接口无需角色
        if (CollectionUtils.isEmpty(roles)) {
            return Mono.just(new AuthorizationDecision(true));
        }
        // 接口角色列表不为空 匿名用户不能访问
        CurrentAuthUser principal = (CurrentAuthUser) authentication.getPrincipal();
        if (principal.isAnonymous()) {
            return Mono.error(new AnonymousAccessDeniedException("未发现用户凭证，须登录访问！"));
        }
        // 有一个角色匹配就通过 返回为空表示该接口可以匿名访问
        for (String role : roles) {
            for (GrantedAuthority authority : authentication.getAuthorities()) {
                if (role.equals(authority.getAuthority())) {
                    return Mono.just(new AuthorizationDecision(true));
                }
            }
        }
        // 没有匹配角色
        return Mono.empty();
    }

    @Override
    public Mono<Void> verify(Mono<Authentication> authentication, String url) {
        return this.check(authentication, url)
                .switchIfEmpty(Mono.error(new AccessDeniedException("无权限！")))
                .flatMap(decision -> Mono.empty());
    }
}
